/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package unmsm.fisi.tesis.si;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Vector;

import unmsm.fisi.tesis.dao.FitnessDAO;
import unmsm.fisi.tesis.dao.GeneracionDAO;
import unmsm.fisi.tesis.dao.PoblacionDAO;
import unmsm.fisi.tesis.entidad.Configuracion;
import unmsm.fisi.tesis.entidad.Cromosoma;
import unmsm.fisi.tesis.entidad.CromosomaFinal;
import unmsm.fisi.tesis.entidad.Poblacion;

/**
 * 
 * @author Jhon
 */
public class AlgoritmoGenetico implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7407069277398936810L;
	private Poblacion poblacion;
	private Operadores operadores;
	private Seleccion seleccion;
	private Cromosoma cromosomaSeleccionada_1;
	private Cromosoma cromosomaSeleccionada_2;

	private StringBuilder seguimiento;
	
	private long tiempoInicial;
	private long tiempoFinal;
	private boolean flagIngreso=false;
	private boolean condicionParada=false;

	/** @param Configuracion, Pruebas :) @return ...retorna Vacio.. */
	public void ejecutar(Configuracion configuracion) {

		this.setSeguimiento(new StringBuilder());
		this.poblacion = new Poblacion();
		this.seleccion = new Seleccion();
		this.seleccion.setSeguimiento(this.getSeguimiento());
		this.seleccion.setFitnnesMayores(new Vector());
		this.seleccion.getFitnnesMayores().addElement("0".toString());
		this.operadores = new Operadores();

		int contadorHijos = 0, contadorGeneraciones, contadorIteraciones = 0;
		int poblacionInicial;
		double[] frecuenciasAcumuladas;
		List<Cromosoma> ListaCromosomasHijos = new ArrayList<Cromosoma>();
		
		Carga cargaInformacion = new Carga();
		if (configuracion.getMetodo().compareTo("run") == 0) {
			
			limpiarInformacionBaseDatos();
			poblacion = cargaInformacion.generarPoblacionInicial(configuracion);
			Constantes.ULTIMO_NUMERO_GENERACION_FINAL=1;
		} else {

			poblacion = obtenerUltimaPoblacionDesdeBaseDeDatos();
		}
		
		contadorGeneraciones = Constantes.ULTIMO_NUMERO_GENERACION_FINAL;
		configuracion.setNumGeneraciones(Constantes.ULTIMO_NUMERO_GENERACION_FINAL + configuracion.getNumGeneraciones());
		
		if(configuracion.getCondicionParada().trim().equals("Maximo Numero Generaciones")){
			configuracion.setDatoParada(configuracion.getNumGeneraciones());
		}

		seleccion.cargarDataPacientesParaFitness(configuracion.getNumeroPacientes());;
		// seleccion.evaluarValorCromosoma(configuracion,
		// poblacion.getListaCromosomas());

		double valorAdaptacionMaximo = 0.0;
		
		Calendar cal1 = Calendar.getInstance();
		tiempoInicial = cal1.getTimeInMillis();
		
		while (configuracion.isCumpleCondicion(tiempoInicial, tiempoFinal, contadorGeneraciones, valorAdaptacionMaximo)) {

			flagIngreso =true;
			pintarMensajes("*************** GENERACION # "+ contadorGeneraciones + "*******************");

			contadorHijos = 0; contadorIteraciones = 1;

			seleccion.evaluarValorCromosoma(configuracion,	poblacion.getListaCromosomas());
			poblacionInicial = poblacion.getTamanioPoblacion();
			
			frecuenciasAcumuladas = seleccion.calcularfrecuenciasAcumuladas(configuracion, this.poblacion.getListaCromosomas());
			
			do {
				pintarMensajes("---- ITERACION #" + contadorIteraciones);

				double random_r = (double) (Math.random());
				double randomEstGener = 0.0;
				double randomEstGenerMuta = 0.0;
				double random1=0.0;
				double random2=0.0;

				if (random_r <= configuracion.getProbabilidadCrossover_x()) {

					pintarMensajes("(" + random_r + " >= Realiza Crossover)");
					
					if(configuracion.getMetodoSeleccion().trim().equals("metodoRuleta")){
						cromosomaSeleccionada_1 = seleccion.metodoRuleta(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas);
						cromosomaSeleccionada_2 =seleccion.metodoRuleta(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas);
					}
					
					if(configuracion.getMetodoSeleccion().trim().equals("metodoEstocastico")){
						randomEstGener = (double) (Math.random());
						random1= (randomEstGener + 1 -1)/(double)2;
						random2= (randomEstGener + 2 -1)/(double)2;
						
						cromosomaSeleccionada_1 = seleccion.metodoEstocasticoUniversal(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas,random1);
						cromosomaSeleccionada_2 =seleccion.metodoEstocasticoUniversal(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas,random2);
					}
					
					if(configuracion.getMetodoSeleccion().trim().equals("metodoTorneo")){
						cromosomaSeleccionada_1 = seleccion.metodoTorneo(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas);
						cromosomaSeleccionada_2 =seleccion.metodoTorneo(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas);
					}
					
					pintarMensajes("      Padre 1  y Padre 2");
					
					if(configuracion.getOperadorCroosover().trim().equals("metodoMultipunto1"))
						ListaCromosomasHijos = operadores.crossover1(cromosomaSeleccionada_1, cromosomaSeleccionada_2, contadorGeneraciones,contadorIteraciones);
					
					if(configuracion.getOperadorCroosover().trim().equals("metodoMultipunto2"))
						ListaCromosomasHijos = operadores.crossover2(cromosomaSeleccionada_1, cromosomaSeleccionada_2,  contadorGeneraciones,contadorIteraciones);
					
					if(configuracion.getOperadorCroosover().trim().equals("metodoMultipunto3"))
						ListaCromosomasHijos = operadores.crossover3(cromosomaSeleccionada_1, cromosomaSeleccionada_2,  contadorGeneraciones,contadorIteraciones);
					
					if(configuracion.getOperadorCroosover().trim().equals("metodoMultipunto4"))
						ListaCromosomasHijos = operadores.crossover4(cromosomaSeleccionada_1, cromosomaSeleccionada_2, contadorGeneraciones,contadorIteraciones);
					
					
					seleccion.evaluarValorCromosoma(configuracion,	ListaCromosomasHijos);
					this.poblacion.agregarHijos(ListaCromosomasHijos);

					contadorHijos = contadorHijos + ListaCromosomasHijos.size();

					pintarMensajes("      Hijo Cruzado 1    y    Hijo Cruzado 2");

					ListaCromosomasHijos = null;
					cromosomaSeleccionada_1 = null;
					cromosomaSeleccionada_2 = null;
					cromosomasSeleccionadasAFalse();

				}
				if (random_r <= configuracion.getProbabilidadMutacion_y()) {

					if(configuracion.getMetodoSeleccion().trim().equals("metodoRuleta"))
						cromosomaSeleccionada_1 = seleccion.metodoRuleta(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas);
					
					if(configuracion.getMetodoSeleccion().trim().equals("metodoTorneo"))
						cromosomaSeleccionada_1 = seleccion.metodoTorneo(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas);
					
					if(configuracion.getMetodoSeleccion().trim().equals("metodoEstocastico")){
						//do{
							randomEstGenerMuta = (double) (Math.random());
							random1= (randomEstGenerMuta + 1 -1)/(double)1;
						//}while(Math.floor(randomEstGenerMuta*100) == Math.floor(randomEstGener*100));
						
						cromosomaSeleccionada_1 = seleccion.metodoEstocasticoUniversal(configuracion,this.poblacion.getListaCromosomas(),frecuenciasAcumuladas,random1);
					}
					
					if(configuracion.getMetodoSeleccion().trim().equals("metodoPunto1")){
						ListaCromosomasHijos = operadores.mutacion1Punto(cromosomaSeleccionada_1,contadorGeneraciones,contadorIteraciones);
					}else{
						ListaCromosomasHijos = operadores.mutacion2Puntos(cromosomaSeleccionada_1, contadorGeneraciones,contadorIteraciones);
					}
					
					seleccion.evaluarValorCromosoma(configuracion,	ListaCromosomasHijos);
					this.poblacion.agregarHijoMutado(ListaCromosomasHijos);
					contadorHijos = contadorHijos + ListaCromosomasHijos.size();

					pintarMensajes("(" + random_r + ">= Realiza Mutacion)");
					pintarMensajes("      Padre a Mutar");
					pintarMensajes("      Hijo Mutado");

					ListaCromosomasHijos = null;
					cromosomaSeleccionada_1 = null;
					cromosomasSeleccionadasAFalse();
				}

				/*for (Cromosoma cromosoma : poblacion.getListaCromosomas()) {
					cromosoma.setFlagSeleccion(false);
				}*/

				contadorIteraciones++;

			} while (contadorHijos <= configuracion.getNumeroPoblacion());//

			this.poblacion.agregarHijosAPoblacion(this.poblacion.getListaHijosCromosomas());

			guardarInformacionDeGeneracion(contadorGeneraciones,contadorIteraciones, poblacionInicial,configuracion.getCodigoConfiguracion());

			Poblacion nueva_poblacion = new Poblacion();
			List<Cromosoma> listaNuevasCromosomas = seleccion.seleccionarNuevaPoblacion(poblacion, configuracion, contadorGeneraciones);
			nueva_poblacion.setListaCromosomas(listaNuevasCromosomas);
			this.poblacion = null;
			this.poblacion = nueva_poblacion;
			this.poblacion.setTamanioPoblacion(nueva_poblacion.getListaCromosomas().size());
			
			valorAdaptacionMaximo = listaNuevasCromosomas.get(0).getValorAdaptacion();
			contadorGeneraciones++;
			
			Calendar cal2 = Calendar.getInstance();
			tiempoFinal= cal2.getTimeInMillis();
			
			pintarMensajes("[ " +  valorAdaptacionMaximo*100 + " ] " + "  <  "  +  configuracion.getDatoParada()   );
		}

		guardarUltimaPoblacion(poblacion, contadorGeneraciones-1);
		guardarDatosDeAlgoritmo();

	}

	public void pintarMensajes(String mensaje) {

		System.out.println(mensaje);
		
	}
	public void cromosomasSeleccionadasAFalse(){
		this.poblacion.getListaCromosomas().get(Constantes.posicionEligidaHijo1).setFlagSeleccion(false);
		if(Constantes.posicionEligidaHijo2!=-1)
			this.poblacion.getListaCromosomas().get(Constantes.posicionEligidaHijo2).setFlagSeleccion(false);
			
		Constantes.posicionEligidaHijo1 =-1;
		Constantes.posicionEligidaHijo2=-1;
	}

	public void guardarInformacionDeGeneracion(int numGeneracion, int iteraciones, int poblacionInicial, int codigoConfiguracion) {
		String detalle = "Geracion #" + numGeneracion;
		PoblacionDAO.asignarGeneracionYPoblacion(numGeneracion, detalle,	iteraciones, poblacionInicial, this.poblacion.getTamanioPoblacion(), codigoConfiguracion);
	}

	public void guardarUltimaPoblacion(Poblacion poblacion, int numGeneracion) {

		String cadena;
		for (Cromosoma individuo : poblacion.getListaCromosomas()) {
			cadena = convertitMatrizACadena(individuo.getConocimiento());
			PoblacionDAO.guardarCromosomasFinalesxGeneracion(cadena, numGeneracion, Constantes.NUMERO_CROMOSOMAS -1 , individuo);

		}
	}
	
	public Poblacion obtenerUltimaPoblacionDesdeBaseDeDatos(){
		
		List<CromosomaFinal> ListaCromosomaFinal= PoblacionDAO.obtenerUltimaPoblacionDesdeBaseDeDatos();
		return crearPobacionDesdeCromosomasFinales(ListaCromosomaFinal);
		
	}
	
	public Cromosoma obtenerUltimoCromosoma(){
		
		CromosomaFinal cromosomaFinal= PoblacionDAO.obtenerMejorCromosomaDeUltimaPoblacion();
		return obtenerCromosomaFinal(cromosomaFinal);
		
	}

	public void guardarDatosDeAlgoritmo(){
		
		GeneracionDAO.guardarResumenDeAlgoritmo();
		FitnessDAO.guardarNacimientoDeFitnes();
		FitnessDAO.guardarCantidadCromosomasEnFitnnes();
	}
	
	
	public Poblacion crearPobacionDesdeCromosomasFinales(List<CromosomaFinal> ListaCromosomaFinal){
		
		Poblacion poblacion= new Poblacion();
		Cromosoma crom;
		List<Cromosoma> listaCromosomas= new ArrayList<Cromosoma>();
		
		for (CromosomaFinal cromosomafinal : ListaCromosomaFinal) {
			
			crom = new Cromosoma();
			int [][]conocimiento = convertitCadenaAMatriz(cromosomafinal.getNumerofila(), cromosomafinal.getNumerocolumna(),
					cromosomafinal.getConocimiento());
			crom.setConocimiento(conocimiento);
			crom.setNumeroCromosoma(cromosomafinal.getNumeroCromosoma());
			Constantes.NUMERO_CROMOSOMAS= cromosomafinal.getUltimoCromosoma() +1 ;
			Constantes.ULTIMO_NUMERO_GENERACION_FINAL = cromosomafinal.getNumeroGeneracion() + 1;
			listaCromosomas.add(crom);
		}
		
		
		poblacion.setListaCromosomas(listaCromosomas);
		poblacion.setTamanioPoblacion(listaCromosomas.size());
		
		return poblacion;
	} 
	
  public Cromosoma obtenerCromosomaFinal(CromosomaFinal cromosomaFinal){
		
		Cromosoma crom = new Cromosoma();
		int [][]conocimiento = convertitCadenaAMatriz(cromosomaFinal.getNumerofila(), 
				cromosomaFinal.getNumerocolumna(),
				cromosomaFinal.getConocimiento());
		crom.setConocimiento(conocimiento);
		crom.setNumeroCromosoma(cromosomaFinal.getNumeroCromosoma());
		crom.setResultadoSistema(cromosomaFinal.getResultado());
		crom.setPosicionesReglasAcertadas(cromosomaFinal.getPosicionesReglasAcertadas());
		
		return crom;
	} 


	public static String convertitMatrizACadena(int conocimiento[][]) {

		StringBuffer cadena = new StringBuffer();

		for (int x = 0; x < conocimiento.length; x++) {

			for (int y = 0; y < conocimiento[0].length; y++) {

				cadena.append(conocimiento[x][y]);
				// cadena.append(",");

			}
			cadena.append("-");

		}

		String cadena2 = cadena.toString().substring(0,
				cadena.toString().length() - 2);

		return cadena2;
	}

	public static int[][] convertitCadenaAMatriz(int fila, int columna, String data) {

		int matraiz[][] = new int[fila][columna];

		String[] datosFila = data.toString().split("-");

		for (int x = 0; x < datosFila.length; x++) {
			String cadena = datosFila[x];
			char[] datosColumn = cadena.toCharArray();

			for (int y = 0; y < datosColumn.length; y++) {
				char val = datosColumn[y];
				matraiz[x][y] = Integer.parseInt(Character.toString(val));
			}

		}
		return matraiz;
	}

	public void limpiarInformacionBaseDatos() {

		FitnessDAO objFitnes = new FitnessDAO();
		objFitnes.eliminarGeneracionCromosomaPoblacionFitnes();
	}

	public boolean condicion() {
		return true;
	}

	public void mostrarCromosomaSeleccionados(int conocimiento[][]) {

		// System.out.println("Conocimiento #"+(i+1)+"\n");
		for (int j1 = 0; j1 < conocimiento.length; j1++) {

			for (int j2 = 0; j2 < conocimiento[j1].length; j2++) {
				System.out.print(conocimiento[j1][j2] + ",");
				
			}
			System.out.print("\n");
		

		}

		System.out.print("\n");
		System.out.print("\n");

	}

	public StringBuilder getSeguimiento() {
		return seguimiento;
	}

	public void setSeguimiento(StringBuilder seguimiento) {
		this.seguimiento = seguimiento;
	}

	public Seleccion getSeleccion() {
		return seleccion;
	}

	public void setSeleccion(Seleccion seleccion) {
		this.seleccion = seleccion;
	}

}
